package xyz.cerberu.cet.user.service;

import com.bete.MD5Utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xyz.cerberu.cet.global.service.EmailService;
import xyz.cerberu.cet.user.dao.UserDao;
import xyz.cerberu.cet.user.domain.Role;
import xyz.cerberu.cet.user.domain.User;
import xyz.cerberu.cet.user.exception.EmailExistException;
import xyz.cerberu.cet.user.exception.RepetitionSendMailException;

import javax.mail.MessagingException;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author β世界
 * Created on 17:34
 * @Description 用户服务实现
 */
@Service
public class UserServiceImpl {
    private final static int VERIFY_TIMEOUT = 60;
    /** 验证码的种子(1-9和A-Z) **/
    private final static char[] MODEL = "3GHI4XYL12M567NVDE89ABCTUWOPQRFJKSZ".toCharArray();
    /** 注册验证码前缀 **/
    private final static String VERIFY = "verify-";
    /** 忘记密码验证码前缀 **/
    private final static String FORGET = "forget-";
    /** redis的操作类 **/
    private final RedisTemplate<String, Object> redisTemplate;
    private final EmailService emailService;
    private final UserDao userDao;

    @Value("${global.remember-me-max-age}")
    public int maxAge;

    @Autowired
    public UserServiceImpl(RedisTemplate<String, Object> redisTemplate, EmailService emailService, UserDao userDao) {
        this.redisTemplate = redisTemplate;
        this.emailService = emailService;
        this.userDao = userDao;
    }

    /**
     * @author β世界 on 10:44 2020/6/14
     * 用于处理登录状态，需要去查找数据库是否有相同的用户名,如果有就返回1
     * 没有就将用户的信息插入到数据库，插入完成就返回2
     * @param user: 用户信息
     * @return int
     **/
    @Transactional(rollbackFor = {Exception.class})
    public int register(User user){
        int statusCode = 0;

        try{
            // 生成用户的ID
            user.setUid(MD5Utils.stringToMD5(user.getPassword()+ UUID.randomUUID().toString()));
            // 设置默认权限
            user.setRole(new Role(1));
            // 再次加密密码(根据邮箱加盐)
            ByteSource byteSource = ByteSource.Util.bytes(user.getEmail());
            user.setPassword(new SimpleHash("MD5",user.getPassword(),byteSource).toString());
            // 注册成功
            userDao.insertUser(user);
            // 设置为成功注册的状态码
            statusCode = 1;
        }catch (Exception e) {
            e.printStackTrace();
            // 将请求状态设为服务出错
            statusCode = 3;
        }

        // 返回状态码
        return statusCode;
    }

    /**
     * @author β世界 on 18:14 2021/3/12
     * 查询一个用户，通过邮箱
     * @param email: 用户的邮箱
     **/
    public User findUser(String email){
        return userDao.findByEmail(email);
    }

    /**
     * @author β世界 on 16:58 2020/6/14
     * 更新基本的用户信息
     * @param user: 用户的实体
     * @return int
     **/
    public int updateBaseInfo(User user) {
        return userDao.updateBaseInfo(user);
    }

    /**
     * @author β世界 on 20:10 2021/3/12
     * 更新用户的密码
     * @param user: 用户实体
     * @return int
     **/
    public int updatePassword(User user){
        ByteSource byteSource = ByteSource.Util.bytes(user.getEmail());
        user.setPassword(new SimpleHash("MD5",user.getPassword(),byteSource).toString());
        return userDao.updatePassword(user);
    }

    /**
     * 刷新用户信息
     * @author β世界 on 12:55 2021/6/4
     * @param principalCollection: 用户的主体集合
     * @param token: 令牌
     * @return void
     **/
    public void flushUser(PrincipalCollection principalCollection,String token){
        String realm = principalCollection.getRealmNames().stream().iterator().next();
        User user = (User) principalCollection.getPrimaryPrincipal();

        HashMap<String,Object> hashMap = new HashMap<>();
        hashMap.put("user",user);
        hashMap.put("realm",realm);
        redisTemplate.opsForValue().set(token,hashMap, maxAge,TimeUnit.SECONDS);
    }

    /**
     * @author β世界 on 21:24 2020/7/1
     * 通过发送一个注册的验证码到用户的邮箱中，来确认这个邮箱是否正确
     * @param email: 邮箱信息
     **/
    public void verifyEmail(String email,Integer type) throws MessagingException {
        String key = "";

        // 检测验证码类型
        switch (type){
            // 注册
            case 1:
                // 检查当前邮箱是否存在
                if(userDao.findByEmail(email) != null){
                    throw new EmailExistException("邮箱已被注册~");
                }else{
                    key = VERIFY+email;
                }
                break;
            // 忘记密码
            case 2:
                // 检查当前这个邮箱是否存在
                if(userDao.findByEmail(email)!= null){
                    key = FORGET+email;
                }else{
                    throw new EmailExistException("邮箱不存在");
                }
                break;
            default:;
        }

        // 检查是否过已经发送过(检查重复key)
        if(redisTemplate.hasKey(key)){
            // 如果存在就检查是否超过1分钟
            long time = 300-redisTemplate.getExpire(key);
            if(time <= VERIFY_TIMEOUT){
                throw new RepetitionSendMailException("重复发送验证码,请在"+(60-time)+"秒后再试");
            }
        }

        // 生成验证码
        String randomCode = randomCode();
        // 发送邮件
        emailService.sendRegisterVerifyCode(randomCode,email);
        // 将注册码存入Redis
        redisTemplate.opsForValue().set(key, randomCode,5,TimeUnit.MINUTES);
    }

    /**
     * @author β世界 on 22:05 2020/7/1
     * 检查验证码是否正确
     * @param email: 邮箱
     * @param userCode: 用户输入的验证码
     * @param type: 验证码的类型
     * @return boolean
     **/
    public boolean checkCode(String email,String userCode,Integer type) {
        String key = "";
        // 检测验证码类型
        switch (type){
            // 注册
            case 1:
                key = VERIFY+email;
                break;
            // 忘记密码
            case 2:
                // 检查当前这个邮箱是否存在
                key = FORGET+email;
                break;
            default:;
        }
        // 获取一个用户的验证码，并且删除redis的验证码
        String redisCode = (String) redisTemplate.opsForValue().get(key);
        redisTemplate.delete(key);
        return StringUtils.equalsIgnoreCase(userCode,redisCode);
    }

    /**
     * @author β世界 on 14:02 2020/11/5
     * 生成随机验证码
     **/
    private String randomCode(){
        StringBuilder randomCode = new StringBuilder();
        // 生存随机数
        for (int j = 0; j < 6; j++) {
            char c = MODEL[(int) (Math.random() * 34)];
            randomCode.append(c);
        }
        return randomCode.toString();
    }

}
